package com.ming_za.odupdaterserver.core.service.update;

import com.ming_za.odupdaterserver.api.*;
import com.ming_za.odupdaterserver.core.config.ConfigManager;
import com.ming_za.odupdaterserver.core.config.type.IntValue;
import com.ming_za.odupdaterserver.core.exception.BusinessException;
import com.ming_za.odupdaterserver.core.exception.ROEnum;
import com.ming_za.odupdaterserver.core.service.ReturnBusinessPackage;
import com.ming_za.odupdaterserver.core.service.res.obj.MainUpdateItem;
import com.ming_za.odupdaterserver.core.service.res.obj.VersionItem;
import com.ming_za.odupdaterserver.core.service.update.res.PreUpdate;
import com.ming_za.odupdaterserver.core.service.update.res.ResDownload;
import com.ming_za.odupdaterserver.core.service.update.res.UpdateRequest;
import com.ming_za.odupdaterserver.core.sqlite.domain.Label;
import com.ming_za.odupdaterserver.core.sqlite.domain.MainVersion;
import com.ming_za.odupdaterserver.core.sqlite.domain.Res;
import com.ming_za.odupdaterserver.core.utils.FileUtils;
import com.ming_za.odupdaterserver.core.vo.req.ResAddVo;
import com.ming_za.odupdaterserver.core.vo.req.SetResClientPathVo;
import com.ming_za.odupdaterserver.core.vo.req.SetResLabelVo;
import com.ming_za.odupdaterserver.core.vo.resp.ResAssociatedVersionResp;
import com.ming_za.odupdaterserver.core.vo.resp.VersionInfoResp;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.*;

/**
 * 更新器实现类
 * <p>所有更新相关功能都由该类实现</p>
 * <p>如需调用更新功能请查看{@link com.ming_za.odupdaterserver.api.Updater}</p>
 */
@Service
public class ODUpdater implements Updater {

    private final ResourcesManager resourcesManager;
    private final MainVersionManager mainVersionManager;
    private final LabelManager labelManager;
    private final FileUtils fileUtils;

    /**
     * 更新监听器，用于发布生命周期
     */
    private final UpdateListener updateListener;

    public ODUpdater(ResourcesManager resourcesManager, MainVersionManager mainVersionManager, LabelManager labelManager, FileUtils fileUtils, UpdateListener updateListener) {
        this.resourcesManager = resourcesManager;
        this.mainVersionManager = mainVersionManager;
        this.labelManager = labelManager;
        this.fileUtils = fileUtils;
        this.updateListener = updateListener;
    }

    private boolean isPublished(long resId){
        Map<Integer, MainVersion> mainUpdateItems = mainVersionManager.getMainUpdateItems();

        for (Map.Entry<Integer, MainVersion> entry : mainUpdateItems.entrySet()){
            List<Long> ids = entry.getValue().getRes();
            for (Long id : ids){
                if (id.equals(resId)){
                    return entry.getValue().isPublished();
                }
            }
        }

        return false;
    }

    /**
     * 启动单个资源更新
     */
    @Override
    public ReturnBusinessPackage<ResDownload> updateRes(long resId, String ip,boolean checkPublished) {

        Res targetRes = resourcesManager.getRes(resId);

        if (checkPublished && targetRes != null && isPublished(resId)) {
//            throw new BusinessException(ROEnum.UPDATE_RES_UNPUBLISHED);
            return new ReturnBusinessPackage<>(ROEnum.UPDATE_RES_UNPUBLISHED);
        }

        PreUpdate pu = new PreUpdate(
                targetRes != null && (targetRes.getLocalPath() != null && !targetRes.getLocalPath().isEmpty()) && new File(targetRes.getLocalPath()).exists(),
                new UpdateRequest(
                        UpdateState.BEFORE,
                        targetRes,
                        null
                ),
                true);

        //发布更新监听
        updateListener.beforeUpdate(pu,ip);

        //判断是否需要更新
        if (!pu.isFind()){
//            throw new BusinessException(ROEnum.UPDATE_NOTFOUND_RES);
            return new ReturnBusinessPackage<>(ROEnum.UPDATE_NOTFOUND_RES);
        }
        if (!pu.isUpdate()){
//            throw new BusinessException(ROEnum.UPDATE_RES_INTERCEPT);
            return new ReturnBusinessPackage<>(ROEnum.UPDATE_RES_INTERCEPT);
        }

        pu.getRes().setState(UpdateState.PRE_UPDATE);

        //加载文件
        try {
            FileInputStream fis = resourcesManager.getResInputStream(resId);
            if (fis == null) {
                pu.getRes().setState(UpdateState.ERROR);
                Exception e = new BusinessException(ROEnum.UPDATE_NOTFOUND_RES);
                updateListener.error(pu.getRes(),ip,e);
                throw e;
            }
            pu.getRes().setFileInputStream(fis);
        }catch (Exception e){
            pu.getRes().setState(UpdateState.ERROR);
            updateListener.error(pu.getRes(),ip,e);
            throw new BusinessException(e.getMessage(),e,ROEnum.UNEXPECTED_ERROR);
        }

        //设置状态并发布更新事件
        pu.getRes().setState(UpdateState.UPDATE);
        updateListener.onUpdate(pu.getRes(),ip);

//        return new ResDownload(fileUtils.getFileName(pu.getRes().getRes().getClientPath()),pu.getRes().getFileInputStream(),pu.getRes());
        return new ReturnBusinessPackage<>(true,ROEnum.SUCCESS,new ResDownload(fileUtils.getFileName(pu.getRes().getRes().getClientPath()),pu.getRes().getFileInputStream(),pu.getRes()));
    }

    /**
     * 获取传入资源对版本的影响
     * @param resId 资源数组
     * @return 资源版本对应列表
     */
    @Override
    public List<ResAssociatedVersionResp> getInfluenceOfResOnVersion(Long[] resId){
        List<ResAssociatedVersionResp> versions = new ArrayList<>();

        for (Long id:resId){
            versions.add(new ResAssociatedVersionResp(id,mainVersionManager.getVersionOfRes(id)));
        }

        return versions;
    }

    /**
     * 获取存在传入标签的所有资源
     * @param label 单个标签
     * @return 资源id列表
     */
    @Override
    public ReturnBusinessPackage<List<Long>> getInfluenceOfLabelOnRes(long label){

        List<Res> res;

        if (label == 0){
            res = resourcesManager.getDefaultLabelRes();
        }else {
            ReturnBusinessPackage<List<Res>> rbp = resourcesManager.getResIncludeLabel(label);
            if (!rbp.isSuccess()){
                return new ReturnBusinessPackage<>(false,rbp.getCode(),null);
            }

            res = rbp.getData();
        }
        List<Long> regIds = new ArrayList<>();

        for (Res re : res){
            regIds.add(re.getResid());
        }
//        return regIds;
        return new ReturnBusinessPackage<>(true,ROEnum.SUCCESS,regIds);
    }

    /**
     * 获取传入标签受影响的所有子级
     * @param label 单个标签
     */
    @Override
    public ReturnBusinessPackage<List<Label>> getInfluenceOfLabelOnExtends(long label){
        if (label == 0){
//            return new ArrayList<>();
            return new ReturnBusinessPackage<>(true,ROEnum.SUCCESS,new ArrayList<>());
        }
//        return labelManager.getLabelSons(label);
        return labelManager.getLabelSons(label);
    }

    /**
     * 获取客户端版本到最新版之间的所有版本
     * @param clientVersion 客户端版本
     */
    @Override
    public List<Integer> getVersionBetweenClientVersion(Integer clientVersion,boolean isPublished){
        return mainVersionManager.getVersions(clientVersion,isPublished);
    }

    @Override
    public ReturnBusinessPackage<List<MainUpdateItem>> getVersionResAll(Integer version) {
        return mainVersionManager.getVersionResAll(version);
    }

    /**
     * 获取指定版本需要更新的资源内容
     * @param version 版本号
     * @param label 标签
     */
    @Override
    public ReturnBusinessPackage<List<MainUpdateItem>> getUpdateRes(Integer version,long label,boolean isPublished,int clientVersion){
        return mainVersionManager.getMainUpdateItems(version,label,isPublished,clientVersion);
    }

    /**
     * 获取所有标签
     */
    @Override
    public List<Label> getAllLabel(){
        return labelManager.getLabels().values().stream().toList();
    }

    @Override
    public List<VersionInfoResp> getAllVersionInfo(){
        return mainVersionManager.getAllVersionInfo();
    }

    /**
     * 添加标签
     * @param name 标签名
     * @param parent 标签父级，可为null
     * @return 返回标签id
     */
    public Long addLabel(String name,Long parent){
        return labelManager.addLabel(name,parent);
    }

    @Override
    public ReturnBusinessPackage<Void> delLabel(long labelId) {
        labelManager.delLabel(labelId);

        //检索与该标签相关的资源，然后减去改标签
        ReturnBusinessPackage<List<Res>> rbp = resourcesManager.getResIncludeLabel(labelId);
        if (!rbp.isSuccess()){
            return new ReturnBusinessPackage<>(false,rbp.getCode(),null);
        }

        List<Res> resIncludeLabel = rbp.getData();
        Set<SetResLabelVo> setResLabelVos = new HashSet<>();
        for (Res res : resIncludeLabel){
            setResLabelVos.add(new SetResLabelVo(res.getResid(),res.getLabel() - labelId));
        }

        resourcesManager.setResLabel(setResLabelVos.toArray(new SetResLabelVo[0]));
        return new ReturnBusinessPackage<>(ROEnum.SUCCESS);
    }

    @Override
    public ReturnBusinessPackage<Void> setLabelParent(long labelId, long parent) {
        return labelManager.setLabelParent(labelId,parent);
    }

    /**
     * 添加资源
     * @param res 资源信息对象
     * @param fileInput 文件输入流
     */
    @Override
    public ReturnBusinessPackage<Long> addRes(ResAddVo res, InputStream fileInput){
        ReturnBusinessPackage<Long> rbp_add = resourcesManager.addRes(res, fileInput);

        if (!rbp_add.isSuccess()){
            return new ReturnBusinessPackage<>(false,rbp_add.getCode(),null);
        }

        Long resId = rbp_add.getData();

        ReturnBusinessPackage<Void> rbp = mainVersionManager.addUnpublishedRes(resId, res.getNecessary());
        if (!rbp.isSuccess()){
            return new ReturnBusinessPackage<>(false,rbp.getCode(),null);
        }
//        return resId;
        return new ReturnBusinessPackage<>(ROEnum.SUCCESS,resId);
    }

    @Override
    public ReturnBusinessPackage<Void> newVersion(){

        ReturnBusinessPackage<Void> rbp = mainVersionManager.newVersion(true);
        if (!rbp.isSuccess()){
            return new ReturnBusinessPackage<>(false,rbp.getCode(),null);
        }
//        return resId;
        return new ReturnBusinessPackage<>(ROEnum.SUCCESS);
    }

    /**
     * 删除资源
     * @param resId 资源id
     */
    @Override
    public ReturnBusinessPackage<Void> delRes(Long[] resId,Integer version){
        resourcesManager.removeRes(resId);
        return mainVersionManager.removeVersionRes(resId, version);
    }

    /**
     * 为资源设置标签
     */
    @Override
    public void setResLabel(SetResLabelVo[] setResLabelVo){
        resourcesManager.setResLabel(setResLabelVo);
    }

    @Override
    public void setClientPath(SetResClientPathVo[] setResClientPathVos) {
        resourcesManager.setClientPath(setResClientPathVos);
    }

    /**
     * 修改未发布版本
     * <p>发布后的版本不可修改</p>
     * <p>由于仅可存在一个未发布版本，所以不需要传入原版本号</p>
     * @param version 新版本号
     */
    @Override
    public ReturnBusinessPackage<Void> modifyVersion(Integer version){
        return mainVersionManager.modifyVersion(version);
    }

    /**
     * 删除版本
     * <pre>删除版本同时删除版本相关的所有资源</pre>
     * @param version 版本号
     */
    @Override
    public ReturnBusinessPackage<Void> removeVersion(Integer version){
        return mainVersionManager.removeVersion(version);
    }

    /**
     * 发布版本
     * <pre>仅可存在一个未发布版本，所以不需要传入版本号</pre>
     */
    @Override
    public ReturnBusinessPackage<Object> publishedVersion(){
        return mainVersionManager.publishVersion();
    }

    @Override
    public List<Integer> getAllVersion() {
        return mainVersionManager.getAllVersions();
    }

    @Override
    public Res getResInfo(Long resId) {
        return resourcesManager.getRes(resId);
    }
}
